#ifndef __UCONFIG_H__
#define __UCONFIG_H__

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "list.h"
#include "lock.h"
#include "wait.h"
#include "log.h"

#include "cJSON.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdbool.h>

#define MAX_ENCODER_CHANNELS        8
#define MAX_VI_CHANNELS    			8
#define MAX_VO_CHANNELS   			2
#define MAX_SCALER_CHANNELS   		4
#define MAX_MOSAIC_CHANNELS   		4
#define MAX_RTP_CHANNELS   			4
#define MAX_DEBUG_CHANNELS   		4

#define MACRO2CHAR(macro) (#macro)

#define CJSON_LOAD_STRING(root, node, string) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            if(__node->valuestring){ \
                strncpy(string, __node->valuestring, sizeof(string)); \
                string[sizeof(string) - 1] = '\0';\
            } \
        } \
    }while(0)

#define CJSON_LOAD_STRING_TO_VALUE(root, node, value, list) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            if(__node->valuestring){ \
                value = 0;\
                for(int i = 0; i < sizeof(list) / sizeof(list[0]); i++){ \
                    if(strcmp(list[i].name,  __node->valuestring) == 0){ \
                        value = list[i].id; \
                        break; \
                    } \
                } \
            } \
        } \
    }while(0)

#define CJSON_LOAD_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            value = __node->valueint; \
        } \
    }while(0)

#define CJSON_LOAD_FLOAT_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            value = __node->valuedouble; \
        } \
    }while(0)

#define CJSON_SET_STRING(root, node, string) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            cJSON_SetString(__node, string); \
        } else { \
            __node = cJSON_CreateString(string); \
            cJSON_AddItemToObject(root, #node, __node); \
        } \
    }while(0)


#define CJSON_SET_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            cJSON_SetIntValue(__node, value); \
        } else { \
            __node = cJSON_CreateNumber(value); \
            cJSON_AddItemToObject(root, #node, __node); \
        } \
    }while(0)

#define CJSON_SET_FLOAT_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            cJSON_SetIntValue(__node, value); \
        } else { \
            __node = cJSON_CreateNumber(value); \
            cJSON_AddItemToObject(root, #node, __node); \
        } \
    }while(0)

#define CJSON_CREATE_NODE(root, name, node) \
    do{ \
        if((node = cJSON_GetObjectItem(root , name)) == NULL){ \
            node = cJSON_CreateObject(); \
            cJSON_AddItemToObject(root, name, node);\
        } \
    }while(0)

#define CJSON_CREATE_ARRAY(root, name, node) \
    do{ \
        if((node = cJSON_GetObjectItem(root , name)) == NULL){ \
            node = cJSON_CreateArray(); \
            cJSON_AddItemToObject(root, name, node);\
        } \
    }while(0)

#define CJSON_CREATE_ARRAY_ITEM(root, index, item) \
    do{ \
        if((item = cJSON_GetArrayItem(root , index)) == NULL){ \
            item = cJSON_CreateObject(); \
            cJSON_AddItemToArray(root, item);\
        } \
    }while(0)

#define IMVS_CS_FLAGS(Flags) (((Flags) & 0xFFFF) << 8)
#define IMVS_RExt_FLAGS(Flags) (((Flags) & 0xFFFF) << 8)
#define IMVS_CS_FLAGS(Flags) (((Flags) & 0xFFFF) << 8)
#define IMVS_RExt_FLAGS(Flags) (((Flags) & 0xFFFF) << 8)

#define IMVS_AVC_PROFILE_IDC_CAVLC_444 44 // not supported
#define IMVS_AVC_PROFILE_IDC_BASELINE 66
#define IMVS_AVC_PROFILE_IDC_MAIN 77
#define IMVS_AVC_PROFILE_IDC_EXTENDED 88 // not supported
#define IMVS_AVC_PROFILE_IDC_HIGH 100
#define IMVS_AVC_PROFILE_IDC_HIGH10 110
#define IMVS_AVC_PROFILE_IDC_HIGH_422 122
#define IMVS_AVC_PROFILE_IDC_HIGH_444_PRED 244 // not supported

#define IMVS_HEVC_PROFILE_IDC_MAIN 1
#define IMVS_HEVC_PROFILE_IDC_MAIN10 2
#define IMVS_HEVC_PROFILE_IDC_MAIN_STILL 3
#define IMVS_HEVC_PROFILE_IDC_RExt 4
typedef enum IMVS_e_Codec
{
  IMVS_CODEC_AVC,
  IMVS_CODEC_HEVC,
  IMVS_CODEC_JPEG,
  IMVS_CODEC_VP9,
  IMVS_CODEC_AV1,
  IMVS_CODEC_INVALID, /* sentinel */
}IMVS_ECodec;
typedef enum IMVS_e_Profile
{
  IMVS_PROFILE_AVC = (IMVS_CODEC_AVC << 24),
  IMVS_PROFILE_AVC_CAVLC_444 = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_CAVLC_444, // not supported
  IMVS_PROFILE_AVC_BASELINE = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_BASELINE,
  IMVS_PROFILE_AVC_MAIN = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_MAIN,
  IMVS_PROFILE_AVC_EXTENDED = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_EXTENDED, // not supported
  IMVS_PROFILE_AVC_HIGH = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_HIGH,
  IMVS_PROFILE_AVC_HIGH10 = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_HIGH10,
  IMVS_PROFILE_AVC_HIGH_422 = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_HIGH_422,
  IMVS_PROFILE_AVC_HIGH_444_PRED = IMVS_PROFILE_AVC | IMVS_AVC_PROFILE_IDC_HIGH_444_PRED, // not supported

  IMVS_PROFILE_AVC_C_BASELINE = IMVS_PROFILE_AVC_BASELINE | IMVS_CS_FLAGS(0x0002),
  IMVS_PROFILE_AVC_PROG_HIGH = IMVS_PROFILE_AVC_HIGH | IMVS_CS_FLAGS(0x0010),
  IMVS_PROFILE_AVC_C_HIGH = IMVS_PROFILE_AVC_HIGH | IMVS_CS_FLAGS(0x0030),
  IMVS_PROFILE_AVC_HIGH10_INTRA = IMVS_PROFILE_AVC_HIGH10 | IMVS_CS_FLAGS(0x0008),
  IMVS_PROFILE_AVC_HIGH_422_INTRA = IMVS_PROFILE_AVC_HIGH_422 | IMVS_CS_FLAGS(0x0008),
  IMVS_PROFILE_AVC_HIGH_444_INTRA = IMVS_PROFILE_AVC_HIGH_444_PRED | IMVS_CS_FLAGS(0x0008), // not supported

  IMVS_PROFILE_HEVC = (IMVS_CODEC_HEVC << 24),
  IMVS_PROFILE_HEVC_MAIN = IMVS_PROFILE_HEVC | IMVS_HEVC_PROFILE_IDC_MAIN,
  IMVS_PROFILE_HEVC_MAIN10 = IMVS_PROFILE_HEVC | IMVS_HEVC_PROFILE_IDC_MAIN10,
  IMVS_PROFILE_HEVC_MAIN_STILL = IMVS_PROFILE_HEVC | IMVS_HEVC_PROFILE_IDC_MAIN_STILL,
  IMVS_PROFILE_HEVC_RExt = IMVS_PROFILE_HEVC | IMVS_HEVC_PROFILE_IDC_RExt,

  IMVS_PROFILE_HEVC_MONO = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xFC80),
  IMVS_PROFILE_HEVC_MONO10 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xDC80),
  IMVS_PROFILE_HEVC_MONO12 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x9C80), // not supported
  IMVS_PROFILE_HEVC_MONO16 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x1C80), // not supported
  IMVS_PROFILE_HEVC_MAIN12 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x9880), // not supported
  IMVS_PROFILE_HEVC_MAIN_422 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xF080),
  IMVS_PROFILE_HEVC_MAIN_422_10 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xD080),
  IMVS_PROFILE_HEVC_MAIN_422_12 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x9080), // not supported
  IMVS_PROFILE_HEVC_MAIN_444 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xE080), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_10 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xC080), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_12 = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x8080), // not supported
  IMVS_PROFILE_HEVC_MAIN_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xFA00),
  IMVS_PROFILE_HEVC_MAIN10_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xDA00),
  IMVS_PROFILE_HEVC_MAIN12_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x9A00), // not supported
  IMVS_PROFILE_HEVC_MAIN_422_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xF200),
  IMVS_PROFILE_HEVC_MAIN_422_10_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xD200),
  IMVS_PROFILE_HEVC_MAIN_422_12_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x9200), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xE200), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_10_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xC200), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_12_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x8200), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_16_INTRA = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x0200), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_STILL = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0xE300), // not supported
  IMVS_PROFILE_HEVC_MAIN_444_16_STILL = IMVS_PROFILE_HEVC_RExt | IMVS_RExt_FLAGS(0x0300), // not supported

} IMVS_EProfile;

typedef enum e_EntropyMode
{
  IMVS_MODE_CAVLC,
  IMVS_MODE_CABAC,
  IMVS_MODE_MAX_ENUM, /* sentinel */
}IMVS_EEntropyMode;

typedef enum IMVS_e_RateCtrlMode
{
  IMVS_RC_CONST_QP = 0x00,
  IMVS_RC_CBR = 0x01,
  IMVS_RC_VBR = 0x02,
  IMVS_RC_LOW_LATENCY = 0x03,
  IMVS_RC_CAPPED_VBR = 0x04,
  IMVS_RC_BYPASS = 0x3F,
  IMVS_RC_PLUGIN = 0x40,
  IMVS_TEST_RC_FILLER = 0xF1,
  IMVS_RC_MAX_ENUM,
} IMVS_ERateCtrlMode;

typedef enum IMVS_e_GopCtrlMode
{
  IMVS_GOP_MODE_DEFAULT = 0x00,
  IMVS_GOP_MODE_PYRAMIDAL = 0x01,
  IMVS_GOP_MODE_ADAPTIVE = 0x02,
  IMVS_GOP_MODE_BYPASS = 0x7F,
  IMVS_GOP_FLAG_LOW_DELAY = 0x80,
  IMVS_GOP_MODE_LOW_DELAY_P = IMVS_GOP_FLAG_LOW_DELAY | 0x00,
  IMVS_GOP_MODE_LOW_DELAY_B = IMVS_GOP_FLAG_LOW_DELAY | 0x01,
  IMVS_GOP_MODE_MAX_ENUM,
} IMVS_EGopCtrlMode;

#define MACRO_STR(x) {x, #x}
typedef struct _macro_str { 
    int id; 
    char *name; 
}MACRO_STR_T;

enum DEVICE_WORK_MODE{
    DEVICE_WORK_MODE_RUNNING = 0,
    DEVICE_WORK_MODE_DEBUG,
    DEVICE_WORK_MODE_UPDATE,
    DEVICE_WORK_MODE_RESERVED,
};

enum ENCODER_MODE{
    ENCODER_H264 = 0,
    ENCODER_H265,
};

enum INTERLACE_MODE{
    INTERLACE_DISCARD = 0,
    INTERLACE_INTERLEAVED,
};

enum PIXEL_FORMAT{
    IMVS_420_8BITS = 0,
    IMVS_422_8BITS,
    IMVS_420_10BITS,
    IMVS_422_10BITS,
};

enum MOSAIC_MODE{
    IMVS_MOSAIC_4X4 = 0,
    IMVS_MOSAIC_8X8,
    IMVS_MOSAIC_16X16,
    IMVS_MOSAIC_32X32,
    IMVS_MOSAIC_64X64,
};

typedef struct {
    uint32_t id;
    uint32_t width;
    uint32_t height;
    char path[256];

    void* priv;
}debug_config_t;

typedef struct {
    uint32_t mode;
    uint32_t cbpsize;
    uint32_t framerate;
    uint32_t targetbitrate;
    uint32_t maxbitrate;
    uint32_t initialqp;
    uint32_t uInitialRemDelay;
    uint32_t minqp;
    uint32_t maxqp;
    uint32_t uMaxPictureSize;
    uint32_t eOptions;
    uint32_t ipdelta;
    uint32_t pbdelta;
}encoder_rc_t;

typedef struct {
    uint32_t mode;
    uint32_t length;
    uint32_t numb;
    uint32_t freqidr;
}encoder_gop_t;

typedef struct {
    uint32_t id;
    uint32_t nbuffer;

    uint32_t enable;
    uint32_t encoder;
    uint32_t width;
    uint32_t height;
    uint32_t bitdepth;
    uint32_t uLevel;
    uint32_t profile;
    uint32_t entropymode;
    uint32_t numslices;
    uint32_t slicesize;

    encoder_rc_t rc;
    encoder_gop_t gop;
}encoder_config_t;

typedef struct {
	uint32_t id;
    uint32_t nbuffer;
    
    uint32_t width;
    uint32_t height;
    uint32_t fps;
    uint32_t interlace;
}input_config_t;

typedef struct {
	uint32_t id;
    uint32_t nbuffer;
    
    uint32_t width;
    uint32_t height;
    uint32_t fps;
    uint32_t enable;
}output_config_t;

typedef struct {
    uint32_t width;
    uint32_t height;
    uint32_t pixfmt;
}scaler_param_t;

typedef struct {
	uint32_t id;
    uint32_t nbuffer;
    
    scaler_param_t in;
    scaler_param_t out;
}scaler_config_t;

typedef struct {
	uint32_t id;
    uint32_t nbuffer;
    
    uint32_t posx;
    uint32_t posy;
    uint32_t width;
    uint32_t height;
    uint32_t mode;
}mosaic_config_t;

typedef struct {
    uint32_t id;
    uint32_t port;
    char ipaddr[32];
}rtp_config_t;

typedef struct {
    uint32_t vi_num;
    uint32_t vo_num;
    uint32_t encoder_num;
    uint32_t scaler_num;
    uint32_t mosaic_num;
    uint32_t rtp_num;
    uint32_t debug_num;

	input_config_t input_config[MAX_VI_CHANNELS];
    scaler_config_t scaler_config[MAX_SCALER_CHANNELS];
    output_config_t output_config[MAX_VO_CHANNELS];
    encoder_config_t encoder_config[MAX_ENCODER_CHANNELS];
    mosaic_config_t mosaic_config[MAX_MOSAIC_CHANNELS];
    rtp_config_t rtp_config[MAX_RTP_CHANNELS];
    debug_config_t debug_config[MAX_DEBUG_CHANNELS];
    
    void *priv;
}config_info_t;

struct uconfig;

enum UCONFIG_EVENT
{
    UCONFIG_EVENT_NONE = 0,
    UCONFIG_EVENT_TEST,
    UCONFIG_EVENT_MAX,
};

typedef int32_t (*uconfig_event_notify)(struct uconfig*, int32_t, void*, void*);

struct uconfig_event_action
{
    uconfig_event_notify notify;
    void* object;
    struct uconfig_event_action* next;
};

struct uconfig_operation
{
    int32_t (*init)(struct uconfig*);
    int32_t (*release)(struct uconfig*);

    int32_t (*start)(struct uconfig*);
    int32_t (*stop)(struct uconfig*);
    
    int32_t (*show)(struct uconfig*);
    int32_t (*load)(struct uconfig*);
    int32_t (*save)(struct uconfig*);

    int32_t (*register_notify)(struct uconfig*, int32_t, uconfig_event_notify notify, void*);
    int32_t (*unregister_notify)(struct uconfig*, int32_t, void*);
    int32_t (*trigger_notify)(struct uconfig*, int32_t, void*);
};

struct uconfig
{
    struct list_head head;
    wait_t wait;
    lock_t lock;
    lock_t notify_lock;

    int running_flag;
    pthread_t pid;
    
    const char *input_config_file;
    const char *default_config_file;
    config_info_t info;

    struct uconfig_operation* op;
    struct uconfig_event_action *paction[UCONFIG_EVENT_MAX];
};

int32_t create_init_uconfig(struct uconfig** puconfig);
void release_destroy_uconfig(struct uconfig* puconfig);
        
#endif

